use std::{cell::RefCell, rc::Rc};

use mlua::{chunk, Lua, MetaMethod, Result, UserData};

#[derive(Default)]
struct Rectangle {
    length: u32,
    width: u32,
}

impl Rectangle {
    pub fn new(length: u32, width: u32) -> Self {
        Self { length, width }
    }
}

impl UserData for Rectangle {
    fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
        fields.add_field_method_get("length", |_, this| Ok(this.length));
        fields.add_field_method_set("length", |_, this, val| {
            this.length = val;
            Ok(())
        });
        fields.add_field_method_get("width", |_, this| Ok(this.width));
        fields.add_field_method_set("width", |_, this, val| {
            this.width = val;
            Ok(())
        });
    }

    fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
        methods.add_method("area", |_, this, ()| Ok(this.length * this.width));
        methods.add_method("diagonal", |_, this, ()| {
            Ok((this.length.pow(2) as f64 + this.width.pow(2) as f64).sqrt())
        });

        // Constructor
        methods.add_meta_function(MetaMethod::Call, |_, ()| Ok(Rectangle::default()));
    }
}

fn main() -> Result<()> {
    let lua = Lua::new();
    let rectangle = Rectangle::new(5, 10);

    let rc_ref_1 = Rc::new(RefCell::new(&rectangle));
    let rc_ref_2 = Rc::new(RefCell::new(rc_ref_1.borrow().clone()));
    println!("{:?}", std::ptr::addr_of!(rc_ref_1));
    println!("{:?}", std::ptr::addr_of!(rc_ref_2));
    println!("{:?}", rc_ref_1.as_ptr());
    println!("{:?}", rc_ref_2.as_ptr());

    // rectangle.length = 5;
    // rectangle.width = 10;
    lua.load(chunk! {
        local rect = $rectangle()
        rect.width = 10
        rect.length = 5
        assert(rect:area() == 50)
        assert(rect:diagonal() - 11.1803 < 0.0001)

        print(getmetatable(rect))
    })
    .exec()
}